package xhttp

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

var (
	ErrHeaderContentType = errors.New("the content-type of request header not include application/json")
)

// HandlerFunc defines the request handler used
type SessionHandlerFunc func(session *Session)

type Session struct {
	// origin objects
	Writer http.ResponseWriter
	Req    *http.Request
	// request info
	Path   string
	Method string
	// response info
	StatusCode int
}

func NewSession(w http.ResponseWriter, req *http.Request) *Session {
	return &Session{
		Writer: w,
		Req:    req,
		Path:   req.URL.Path,
		Method: req.Method,
	}
}

func (s *Session) Request() *http.Request {
	return s.Req
}

func (s *Session) RequestHeader() http.Header {
	return s.Request().Header
}

func (s *Session) ResponseWriter() http.ResponseWriter {
	return s.Writer
}

func (s *Session) GetMethod() string {
	return s.Method
}

func (s *Session) ContentType() string {
	return s.RequestHeader().Get("Content-Type")
}

func (s *Session) ReadRawData() ([]byte, error) {
	return ioutil.ReadAll(s.Req.Body)
}

func (s *Session) ReadBodyJson(v interface{}) error {
	req := s.Req
	if s.ContentType() == "application/json" {
		if err := json.NewDecoder(req.Body).Decode(v); err != nil {
			http.Error(s.Writer, err.Error(), http.StatusInternalServerError)
			return err
		}
		return nil
	}
	err := ErrHeaderContentType
	http.Error(s.Writer, err.Error(), http.StatusBadRequest)
	return err
}

func (s *Session) PostForm(key string) string {
	return s.Req.FormValue(key)
}

func (s *Session) Query(key string) string {
	return s.Req.URL.Query().Get(key)
}

func (s *Session) Status(code int) {
	s.StatusCode = code
	s.Writer.WriteHeader(code)
}

func (s *Session) SetHeader(key string, value string) {
	s.Writer.Header().Set(key, value)
}

func (s *Session) WriteString(code int, format string, values ...interface{}) error {
	s.SetHeader("Content-Type", "text/plain")
	s.Status(code)
	if _, err := s.Writer.Write([]byte(fmt.Sprintf(format, values...))); err != nil {
		return err
	}
	return nil
}

func (s *Session) WriteJSON(code int, obj interface{}) error {
	s.SetHeader("Content-Type", "application/json")
	s.Status(code)
	encoder := json.NewEncoder(s.Writer)
	if err := encoder.Encode(obj); err != nil {
		http.Error(s.Writer, err.Error(), 500)
	}
	return nil
}

func (s *Session) WriteData(code int, data []byte) error {
	s.Status(code)
	if _, err := s.Writer.Write(data); err != nil {
		return err
	}
	return nil
}

func (s *Session) WriteHTML(code int, html string) error {
	s.SetHeader("Content-Type", "text/html")
	s.Status(code)
	if _, err := s.Writer.Write([]byte(html)); err != nil {
		return err
	}
	return nil
}

func (s *Session) WriteError(code int, err error) {
	http.Error(s.Writer, err.Error(), code)
}
